#ifndef BONE_H
#define BONE_H

#include <wx/gdicmn.h>
#include <wx/dc.h>
#include <wx/image.h>

#include <string>
#include <list>

#include "../include/BoneState.h"

class Bone
{
    public:
        enum JOINT {NON=-1, BEGIN=0, END=1};

        /** Constructor of Bone class
        * @param parent - mother bone
        * @param jointNumber - the edge where bone sticks to the parent`s bone (0 - begin, 1 - end)
        * @param name - name of the bone
        * @param nimAngle - minimum relative angle
        * @param maxAngle - maximum relative angle
        */
        Bone (Bone* parent, bool jointNumber, wxString name = wxString() ,double angleMin=0, double angleMax=0);

        /** Constructor of Bone class, should be used only to create rootBone
        * @param *beginJoint - first bone's joint
        * @param *endJoint - second bone's joint
        * @param name - name of the bone
        * @param nimAngle - minimum relative angle
        * @param maxAngle - macimum relative angle
        */
        Bone(wxRealPoint beginJoint, wxRealPoint endJoint, wxString name =_("RBONE"), double angleMin=0, double angleMax=0 );

        /** Destructor */
        ~Bone();

        /** Predicate for stl::find_if algorithm, searching by boneId */
        bool operator()(BoneState* state) { return state->boneId == id; }

        /** Move root's begin point by translate vector - must be used only on rootBone
        * @param translation vector to move root's begin point
        */
        void moveRootBegin(wxRealPoint translation);

        /** Set the end of the joint for the bone
        * @param endJoint - the end of the bone
        * @return void
        */
        void setEnd(wxRealPoint endPoint);

        /** Returns the bone's State
        * @return state of the bone
        */
        BoneState getBoneState(void) const;

        /** Set bone to specified state
        * @param state bone state
        */
        void setBoneState(BoneState state);

        /** Returns the parent bone
        * @return a parent bone
        */
        Bone* getParent() const;

        /** Returns bone's children in stl list container
        * @return reference to stl list of children bones poiters
        */
        std::list<Bone*>& getChildren();


        /** Calculate an angle in terms of parent bone
        * @param void
        * @return an absolute angle (in radians)
        */
        double angleToParent(void);

        /** Calculate the length of the bone
        * @param void
        * @return the length of the bone
        */
        double length(void);

        /** Calculate an angle in terms of external system
        * @param void
        * @return an absolute angle (in radians)
        */
        double angleAbsolute(void);

        /** Sets joint's position basing of the give absolute angle.
        * @param angle - absolute angle, were (0,0) it the top left canvas corner, in radians
        * @return void
        */
        void setAngle(double angle);

        /** Set the minimum angle
        * @param _angleMin - the minimum angle
        * @return void
        */
        void setAngleMin(double _angleMin);

        /** Set the maximum angle
        * @param _angleMax - the maximum angle
        * @return void
        */
        void setAngleMax(double _angleMax);

        /** Rotate the bone by an angle around one of it's joits
        * @param angle an absolute angle of rotation, positive or negative
        * @param joint reference rotation point expressed in join number ( 0 - bone's begin, 1 - it's end )
        * @return void
        */
        void rotate(double angle, JOINT BEGINorEND);

        /** Return the minimum angle
        * @param void
        * @return angleMin - the minimum angle
        */
        double getAngleMin(void) const;

        /** Return the maximum angle
        * @param void
        * @return angleMax - the maximum angle
        */
        double getAngleMax(void) const;

        /** Set the minimum and the maximum of angle
        * @param _angleMin - the minimum angle
        * @param _angleMin - the maximum angle
        * @return void
        */
        void setAngleMinMax(double _angleMin, double _angleMax);

        /** Set the name of the bone
        * @param _name - the name of the bone
        * @return void
        */
        void setName(wxString _name);

        /** Return the name of the bone
        * @param void
        * @return name - the name of the bone
        */
        wxString getName(void) const;


        /** Return total number of bones
        * @return number of bones
        */
        static unsigned int getBonesCounter() { return bonesCounter; }

        /** Returns bone's id */
        unsigned int getBoneId() const;

        /** Draws bone's sketch and aprite (if setuped) on the given dc
        * @param dc any kind of wxDC based dc where bone is going to be drawn on
        */
        void draw(wxDC* dc);

        /** Draws bone's sketch on the given dc
        * @param dc any kind of wxDC based dc where bone is going to be drawn on
        */
        void drawSketch(wxDC* dc);

        /** Draws bone's sketch on the given dc
        * @param dc any kind of wxDC based dc where bone is going to be drawn on
        */
        void drawSprite(wxDC* dc);

        /** Hover specified joint
        * @param joint - joint to be hovered (0 - begin, 1-end)
        */
        void hoverJoint(JOINT BeginEndNon);

        /** Returns hovered joint
        * @return hovered joint (0- begin, 1-end, other - bone is unselected)
        */
        JOINT getHoveredJoint() const;

        /** Return joint number which to bone is sticekd to parent bone
        * @param joint number
        */
        bool getJointNumber() const;

        /** Select specified joint
        * @param joint - joint to be selected (0 - begin, 1-end)
        */
        void selectJoint(JOINT BeginEndNon);


        /** Returns selected joint
        * @return selected joint (0- begin, 1-end, other - bone is unselected)
        */
        int getSelectedJoint() const;

        /** Returns bone's begin
        * @return bone's begining point
        */
        wxRealPoint* getBegin() const;

        /** Returns bone's end
        * @return bone's ending point
        */
        wxRealPoint* getEnd() const;

        /** Return sprite file name
        * @return sprite file name
        */
        wxString getSpriteFileName() const;

        /** Sets bones sprite file name
        * @param sprite file name
        */
        void setSpriteFileName(wxString fileName);

        /** Set's bones sprite image
        * @param sprite wxImage object's sprite
        */
        void setSprite( wxImage sprite );

        /** Set's bones sprite relative position in the terms of the bone's begin joint
        * @param pos sprite's displacement ( in pixels )
        */
        void setSpritePos( wxPoint pos );

        /** Return Sprite position
        * @return sprite position
        */
        wxPoint getSpritePos() const;

        /** Set's bones sprite relative angle in the terms of the bone's begin joint
        * @param angle sprite's angle ( in radious )
        */
        void setSpriteAngle( double angle );

        /** Gets bone's orginal angle */
        double getLoopsOrginal() const;

        /** Gets loops */
        double getLoops() const;

        /** Sets loops */
        void setLoops(double loops);

        /** Returns relative loops
        * @param relative loops
        */
        double getLoopsRelative() const;

        /** Sets loops relative
        * @param loops relative
        */
        void setLoopsRelative(double loopsRelative);

        /** tell if stat bone will be animated from current to next state, or not
        * @return will be animated ?
        */
        bool isMotion() const;

        /** set motion to the bone
        * @param motion flag which tell
        */
        void setMotion(bool motion);

    protected:

    private:

            ////////// !!!! UWAGA !!!! Obrót zakłada, że podamy jako punkt referencyjny początek kości
        ////////// Trzeba to sprytnie rozwiązać i naprawić - i chyba wiem jak!
        /** Rotate the bone by an angle
        * @param referencePoint rotate the bone by angle interms of joint
        * @param angle - an absolute angle of rotation (in radians)
        * @return void
        */
        void rotate(wxRealPoint* referencePoint, double angle);

        /** add angle incresement to entire deltaAngle incresement*/
        void addDeltaAngle(double angle);

        /** Remove child bone to the vector of children bone
        * @param child - pointer to the bone to remove
        * @return number of removed bones
        */
        unsigned int removeChild(Bone* child);

        /** Remove all successors of current bone
        * @return number of removed bones
        */
        unsigned int removeChildren();

        /** Set 0 values to lastId, and bonesCounter*/
        static void resetStaticFields();




        /** unique bones id*/
        const unsigned int id;

        /** bones name */
        wxString name;

        /** begining joint of the bone */
        wxRealPoint* begin;

        /** ending joint of the bone */
        wxRealPoint* end;

        /** the edge where bone sticks to the parent`s bone (0 - begin, 1 - end) */
        bool jointNumber;

        /** number of 2PI's loop,  to compute angle just do   loops * 2 * M_PI */
        double loops;

        /** number of 2PI's loop relative to parentBone */
        double loopsRelative;

        /** determines if bone will animated from cureent to next state */
        bool motion;

        /** the orginal loops of bone, when set when bone is created, by setEnd function */
        double loopsOrginal;

        /** minimum angle of deflection (in loops) */
        double angleMin;

        /** maximum angle of deflection (in loops) */
        double angleMax;

        /** pointer to the parents bone */
        Bone* parent;

        /** vector of pointers to the children bones */
        std::list<Bone*> children;

        /** last bone's id */
        static unsigned int lastId;

        /** number of bone's in sceleton */
        static unsigned int bonesCounter;

        /** the last absolute angle */
        double angleLastAbsolute;

        /** the edge of bone which is already selected (0 - begin, 1 - end, others - unselected) */
        JOINT selectedJoint;

        /** the edge of bone which is already hovered by mouse (0 - begin, 1 - end, others - unhovered) */
        JOINT hoveredJoint;

        /** holds bones original sprite image */
        wxImage spriteOriginal;

        wxImage spritePrepared;

        /** */
        wxString spriteFileName;

        /** sprite's displacement in the terms of the bone's begin joint */
        wxPoint spritePos;

        /** sprite's relative angle in the terms of the bone's begin point */
        double spriteAngle;

        bool spriteChanged;

};

#endif // BONE_H
